#include <stdio.h>
#include <assert.h>

#include "widget.h"
#include "delegate.h"

// (private) size of delegate hash table
static size_t dgsize;
// (private) delegate hash table
static delegate_signal_t **dgmap;

void delegate_initialize(size_t table_size)
{
    int i;
    dgsize = table_size;
    assert(dgsize > 0);

    dgmap = malloc(sizeof(delegate_signal_t)*dgsize);
    assert(dgmap != NULL);

    for(i=0; i<dgsize; i++)
    {
        // create a dummy signal n4
        delegate_signal_t *dummy = malloc(sizeof(delegate_signal_t));
        assert(dummy != NULL);
        dummy->next = dummy;
        dummy->sender = NULL;
        dummy->signal = NO_SIGNAL;
        dummy->receiver_list = NULL;
        dgmap[i] = dummy;
    }
}

EXPORT void delegate_connect(widget_t *sender, signal_t signal,
            widget_t *receiver, void (*slot)(widget_t*,void*))
{
    uint32_t hash = ((uint32_t)sender + (uint32_t)signal)%dgsize;
    
    // search if there are an existring sender.signal node
    delegate_signal_t *itr = dgmap[hash];
    while(itr != itr->next)
    {
        if(itr->signal == signal && itr->sender == sender)
        {
            // found! create a delegate receiver and add it to the list
            delegate_receiver_t *node = malloc(sizeof(delegate_receiver_t));
            assert(node != NULL);
            node->slot = slot;
            node->receiver = receiver;
            node->next = itr->receiver_list;
            itr->receiver_list = node;
            return; // bail out!
        }
        itr = itr->next;
    }
    {
        // not found! create a new signal node
        delegate_signal_t *node = malloc(sizeof(delegate_signal_t));
        assert(node != NULL);
        node->signal = signal;
        node->sender = sender;
        node->next = dgmap[hash];
        dgmap[hash]  = node;

        // add a dummy node to the signal receiver list
        {
            delegate_receiver_t *dummy = malloc(sizeof(delegate_receiver_t));
            assert(dummy != NULL);
            dummy->next = dummy;

            node->receiver_list = dummy;

            // add the signal again, now when there exist a sender.signal node
            delegate_connect(sender, signal, receiver, slot);
        }
    }
}

EXPORT void delegate_signal(widget_t *sender, signal_t signal, void *param)
{
    uint32_t hash = ((uint32_t)sender + (uint32_t)signal) % dgsize;

    // search for sender/signal in the hash table
    delegate_signal_t *sitr = dgmap[hash];
    while(sitr != sitr->next)
    {
        if(sitr->sender == sender && sitr->signal == signal)
        {
            // sender.signal found, iterate the receiver list
            delegate_receiver_t *ritr = dgmap[hash]->receiver_list;
            while(ritr != ritr->next)
            {
                // call the slot
                ritr->slot(ritr->receiver, param);
                ritr = ritr->next;
            }
            return; // all receiver have been called, bail out!
        }
        sitr = sitr->next;
    }
}

void delegate_finalize()
{
    int i;

    for(i=0; i<dgsize; i++)
    {
        delegate_signal_t *sitr = dgmap[i];
        while(sitr != sitr->next)
        {
            // remove all noded in the signal node
            delegate_receiver_t *ritr = sitr->receiver_list;
            while(ritr != ritr->next)
            {
                // remove the receiver node
                delegate_receiver_t *rtmp = ritr;
                ritr = ritr->next;
                free(rtmp);
            }
            // remove the dummy node
            free(ritr);

            // remove the signal node
            delegate_signal_t *stmp = sitr;
            sitr = sitr->next;
            free(stmp);
        }
        // remove the signal dummy node
        free(sitr);
    }
    // remove the hash table
    free(dgmap);
}

